/**
 * @Author: Sagan
 * @Date: 2020/09/24 17:00
 */
package validate

import (
	"gitee.com/big_meteor/godash/arr"
	"gitee.com/big_meteor/godash/validate/constant"
	"gitee.com/big_meteor/godash/validate/module"
	"strconv"
	"strings"
)

func checkItem(v interface{}, key, value string) (bool, error) {
	switch key + constant.RuleValueSep + value {
	case constant.REQUIRE_ZERO:
		return module.Require(v, "zero")
	case constant.BOOLEAN_TRUE:
		return module.BoolTrue(v)
	case constant.BOOLEAN_FALSE:
		return module.BoolFalse(v)
	case constant.JSON_ARRAY:
		return module.JsonArray(v)
	case constant.JSON_OBJECT:
		return module.JsonObject(v)
	}

	switch key {
	case constant.REQUIRE:
		return module.Require(v)
	case constant.INT:
		return module.Int(v), nil
	case constant.FLOAT:
		return module.Float(v), nil
	case constant.BOOLEAN:
		return module.Bool(v), nil
	case constant.ARRAY:
		return module.Array(v), nil
	case constant.EMAIL:
		return module.Email(v)
	case constant.ALPHA:
		return module.Alpha(v)
	case constant.ALPHA_NUM:
		return module.AlphaNum(v)
	case constant.ALPHA_DASH:
		return module.AlphaDash(v)
	case constant.CHS:
		return module.Chs(v)
	case constant.CHS_NUM:
		return module.ChsNum(v)
	case constant.CHS_ALPHA:
		return module.ChsAlpha(v)
	case constant.CHS_ALPHA_NUM:
		return module.ChsAlphaNum(v)
	case constant.CHS_DASH:
		return module.ChsDash(v)
	case constant.IP:
		return module.IP(v)
	case constant.IPV4:
		return module.IPv4(v)
	case constant.IPV6:
		return module.IPv6(v)
	case constant.JSON:
		return module.Json(v)
	case constant.IN:
		return checkIn(v, value)
	case constant.NOT_IN:
		return checkNotIn(v, value)
	case constant.BETWEEN:
		return checkBetween(v, value)
	case constant.NOT_BETWEEN:
		return checkNotBetween(v, value)
	case constant.LENGTH:
		return checkLength(v, value)
	case constant.MAX:
		return checkMax(v, value)
	case constant.MIN:
		return checkMin(v, value)
	case constant.EQ, constant.EQ_alias:
		return checkEq(v, value)
	case constant.NEQ, constant.NEQ_alias:
		return checkNeq(v, value)
	case constant.GT, constant.GT_alias:
		return checkGt(v, value)
	case constant.GTE, constant.GTE_alias:
		return checkGte(v, value)
	case constant.LT, constant.LT_alias:
		return checkLt(v, value)
	case constant.LTE, constant.LTE_alias:
		return checkLte(v, value)
	}

	return false, constant.NewRuleNotSup(key)
}

func parseRule(v string) (key string, value string) {
	if "" == v {
		return
	}

	slice := strings.Split(v, constant.RuleValueSep)
	key = slice[0]

	if 1 < len(slice) {
		value = slice[1]
	}

	return
}

func isRequire(v interface{}, rule []string) (bool, error) {
	if 1 > len(rule) {
		return false, nil
	}

	if has, _ := arr.IndexOf(rule, constant.REQUIRE); has {
		return true, nil
	}

	if has, _ := arr.IndexOf(rule, constant.REQUIRE_ZERO); has {
		return true, nil
	}

	return module.Require(v)
}

func checkIn(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_IN_PARAM
	}

	param := strings.Split(value, ",")
	if 1 > len(param) {
		return false, constant.ERROR_IN_PARAM
	}

	return module.In(v, param)
}

func checkNotIn(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_NOT_IN_PARAM
	}

	param := strings.Split(value, ",")
	if 1 > len(param) {
		return false, constant.ERROR_NOT_IN_PARAM
	}

	return module.NotIn(v, param)
}

func checkBetween(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_BETWEEN_PARAM
	}
	param := strings.Split(value, ",")

	var end float64
	var start float64
	var err error

	start, err = strconv.ParseFloat(param[0], 64)
	if nil != err {
		return false, constant.ERROR_BETWEEN_PARAM
	}

	end, err = strconv.ParseFloat(param[1], 64)
	if nil != err {
		return false, constant.ERROR_BETWEEN_PARAM
	}

	return module.Between(v, start, end)
}

func checkNotBetween(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_NOT_BETWEEN_PARAM
	}
	param := strings.Split(value, ",")

	var end float64
	var start float64
	var err error

	start, err = strconv.ParseFloat(param[0], 64)
	if nil != err {
		return false, constant.ERROR_NOT_BETWEEN_PARAM
	}

	end, err = strconv.ParseFloat(param[1], 64)
	if nil != err {
		return false, constant.ERROR_NOT_BETWEEN_PARAM
	}

	return module.NotBetween(v, start, end)
}

func checkLength(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_LENGTH_PARAM
	}

	param := strings.Split(value, ",")
	if 1 > len(param) {
		return false, constant.ERROR_LENGTH_PARAM
	} else if 1 == len(param) {
		length, err := strconv.Atoi(param[0])
		if nil != err {
			return false, constant.ERROR_LENGTH_PARAM
		}

		return module.Length(v, length)
	}

	var end int
	var start int
	var err error

	start, err = strconv.Atoi(param[0])
	if nil != err {
		return false, constant.ERROR_LENGTH_PARAM
	}

	end, err = strconv.Atoi(param[1])
	if nil != err {
		return false, constant.ERROR_LENGTH_PARAM
	}

	return module.LengthBetween(v, start, end)
}

func checkMax(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_MAX_PARAM
	}

	num, err := strconv.Atoi(value)
	if nil != err {
		return false, constant.ERROR_MAX_PARAM
	}

	return module.Max(v, num)
}

func checkMin(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_MIN_PARAM
	}

	num, err := strconv.Atoi(value)
	if nil != err {
		return false, constant.ERROR_MIN_PARAM
	}

	return module.Min(v, num)
}

func checkEq(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_EQ_PARAM
	}

	num, err := strconv.ParseFloat(value, 64)
	if nil != err {
		return false, constant.ERROR_EQ_PARAM
	}

	return module.Eq(v, num)
}

func checkNeq(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_NEQ_PARAM
	}

	num, err := strconv.ParseFloat(value, 64)
	if nil != err {
		return false, constant.ERROR_NEQ_PARAM
	}

	return module.Neq(v, num)
}

func checkGt(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_GT_PARAM
	}

	num, err := strconv.ParseFloat(value, 64)
	if nil != err {
		return false, constant.ERROR_GT_PARAM
	}

	return module.Gt(v, num)
}

func checkGte(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_GTE_PARAM
	}

	num, err := strconv.ParseFloat(value, 64)
	if nil != err {
		return false, constant.ERROR_GTE_PARAM
	}

	return module.Gte(v, num)
}

func checkLt(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_LT_PARAM
	}

	num, err := strconv.ParseFloat(value, 64)
	if nil != err {
		return false, constant.ERROR_LT_PARAM
	}

	return module.Lt(v, num)
}

func checkLte(v interface{}, value string) (bool, error) {
	if "" == value {
		return false, constant.ERROR_LTE_PARAM
	}

	num, err := strconv.ParseFloat(value, 64)
	if nil != err {
		return false, constant.ERROR_LTE_PARAM
	}

	return module.Lte(v, num)
}
